package platform.common.dao.impl;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.MatchMode;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.GenericJDBCException;
import org.hibernate.exception.JDBCConnectionException;
import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.exception.SQLGrammarException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import platform.common.bo.IPersistenceBO;
import platform.common.bo.IdentifiableObj;
import platform.common.bo.Page;
import platform.common.bo.PersistenceBO;
import platform.common.dao.IDao;
import platform.common.dao.IDomainProvider;
import platform.util.Util;

@SuppressWarnings("deprecation")
public abstract class DaoBaseImpl extends HibernateDaoSupport implements IDao {
	/**
	 * log4j进行日志记录。
	 */
	protected Logger log = Logger.getLogger(DaoBaseImpl.class);

	public DaoException handleDaoException(Exception e, String errMsg) {
		if (e instanceof JDBCConnectionException) {
			errMsg = "指明底层的JDBC通讯出现错误。";
			return new CriticalTechnicalDaoException(e);
		} else if (e instanceof SQLGrammarException) {
			errMsg = "指明发送的SQL语句的语法或者格式错误。";
			return new TechnicalDaoException(e);
		} else if (e instanceof ConstraintViolationException) {
			errMsg = "指明某种类型的约束违例错误。";
			return new TechnicalDaoException(e);

		} else if (e instanceof LockAcquisitionException) {
			errMsg = "指明了在执行请求操作时，获取所需的锁级别时出现的错误。";
			return new TechnicalDaoException(e);

		} else if (e instanceof GenericJDBCException) {
			errMsg = "不属于任何其他种类的原生异常。";
			return new TechnicalDaoException(e);

		} else {
			errMsg = "其他类型的异常。";
			return new DaoException(e);
		}
	}

	public DaoException handleDaoException(Exception e) {
		return this.handleDaoException(e, null);
	}

	public DaoConfigException handleDaoConfigException(Exception e,
			String errMsg) {
		if (e instanceof DaoConfigException) {
			errMsg = "没有的领域对象的提供者。";
			return new DaoConfigException(e);
		} else {
			errMsg = "其他类型的异常。";
			return new DaoConfigException(e);
		}
	}

	public DaoConfigException handleDaoConfigException(Exception e) {
		return this.handleDaoConfigException(e, null);
	}

	@SuppressWarnings("unchecked")
	public List getBySql(final String sql, final Class cls) throws DaoException {
		return (List) this.getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException {
						return session.createSQLQuery(sql).addEntity(cls)
								.list();
					}

				});
	}

	/**
	 * @see platform.common.dao.IDao#add(PersistenceBO)
	 */
	public void add(IPersistenceBO object) throws DaoException {
		try {
			log.debug("BaseDao add object ");
			getHibernateTemplate().save(object);
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
	}

	/**
	 * @see platform.common.dao.IDao#saveOrUpdate(PersistenceBO)
	 */
	public Object saveOrUpdate(IPersistenceBO obj) throws DaoException {
		log.debug("BaseDao saveOrUpdate object " + obj);
		try {
			getHibernateTemplate().saveOrUpdate(obj);
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
		return obj;
	}

	/**
	 * @see platform.common.dao.IDao#update(PersistenceBO)
	 */
	public void update(IPersistenceBO object) throws DaoException {
		try {
			log.debug("BaseDao update object " + object);
			getHibernateTemplate().update(object);
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
	}

	/**
	 * @see platform.common.dao.IDao#deleteById(IDomainProvider,Serializable)
	 */
	public void deleteById(IDomainProvider domainProvider, Serializable oid)
			throws DaoException, DaoConfigException {
		try {
			log.debug("BaseDao delete one object "
					+ domainProvider.getDomainClassName());
			IPersistenceBO entity = (IPersistenceBO) findByIdAndDomain(
					domainProvider, oid);
			delete(entity);
		} catch (Exception e) {
			if (e instanceof DaoConfigException) {
				throw (DaoConfigException) e;
			} else {
				throw this.handleDaoException(e);
			}
		}
	}
	
	/**
	 * @see platform.common.dao.IDao#deleteById(IDomainProvider,Serializable)
	 */
	public void deleteById(Serializable oid)throws DaoException, DaoConfigException {
		try {
			log.debug("BaseDao delete one object "+ oid);
			IPersistenceBO entity = (IPersistenceBO) findByIdAndDomain(oid);
			delete(entity);
		} catch (Exception e) {
			if (e instanceof DaoConfigException) {
				throw (DaoConfigException) e;
			} else {
				throw this.handleDaoException(e);
			}
		}
	}

	/**
	 * @see platform.common.dao.IDao#delete(PersistenceBO)
	 */
	public void delete(IPersistenceBO object) throws DaoException {
		try {
			log.debug("BaseDao delete one object " + object);
			getHibernateTemplate().delete(object);
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
	}

	/**
	 * @see platform.common.dao.IDao#findByIdAndDomain(IDomainProvider,Serializable)
	 */
	@SuppressWarnings("unchecked")
	public <T>T findByIdAndDomain(IDomainProvider domainProvider,
			Serializable oid) throws DaoException, DaoConfigException {

		T result = null;
		if (domainProvider == null) {
			throw new DaoConfigException("没有的领域对象的提供者");
		}
		Class<? extends IdentifiableObj> domainClass = domainProvider
				.getDomainClass();
		if (domainClass == null) {
			throw new DaoConfigException("不能找到领域对象的class");
		}
		try {
			result = (T)getHibernateTemplate().get(domainClass, oid);
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
		return result;
	}

	/**
	 * @see platform.common.dao.IDao#findByIdAndDomain(IDomainProvider,Serializable)
	 */
	@SuppressWarnings("unchecked")
	public <T>T findByIdAndDomain(Serializable oid) throws DaoException,
			DaoConfigException {
		T result = null;
		Class<? extends IdentifiableObj> domainClass = ((IDomainProvider) this)
				.getDomainClass();
		if (domainClass == null) {
			throw new DaoConfigException("不能找到领域对象的class");
		}
		try {
			result = (T)getHibernateTemplate().get(domainClass, oid);
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
		return result;
	}

	/**
	 * @see platform.common.dao.IDao#findAllByDomian(IDomainProvider)
	 */
	@SuppressWarnings("unchecked")
	public List findAllByDomian(IDomainProvider domainProvider)
			throws DaoException, DaoConfigException {
		if (domainProvider == null) {
			throw this.handleDaoConfigException(new DaoConfigException(
					"没有的领域对象的提供者"));
		}
		String domainClassName = domainProvider.getDomainClassName();
		List result = null;
		try {
			result = getHibernateTemplate().find("from " + domainClassName);
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
		return result;
	}
	
	/**
	 * @see platform.common.dao.IDao#findAllByDomian(IDomainProvider)
	 */
	@SuppressWarnings("unchecked")
	public List findAllByDomian()throws DaoException, DaoConfigException {
		String domainClassName = ((IDomainProvider)this).getDomainClassName();
		List result = null;
		try {
			result = getHibernateTemplate().find("from " + domainClassName);
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
		return result;
	}

	/**
	 * 
	 * <br/>
	 * 方法描述:依据领域名称查询所有记录,并按分页获取
	 * 
	 * @param domainProvider
	 *            领域提供者
	 * @return 领域列表
	 */
	@SuppressWarnings("unchecked")
	public List findAllByDomian(IDomainProvider domainProvider, Page page)
			throws DaoException, DaoConfigException {
		if (domainProvider == null) {
			throw this.handleDaoConfigException(new DaoConfigException(
					"没有的领域对象的提供者"));
		}
		List result = null;
		try {
			String domainClassName = domainProvider.getDomainClassName();
			Query query = this.getSession().createQuery(
					"from " + domainClassName);
			// 若分页信息提供不全,就不按分页查询
			if (page.getPageNo() > 0 && page.getPageSize() > 0) {
				// 设置分页参数
				query.setFirstResult((page.getPageNo() - 1)
						* page.getPageSize());
				query.setMaxResults(page.getPageSize());
			}
			result = query.list();
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
		return result;
	}

	/**
	 * @see platform.common.dao.IDao#findByHqlNameAndDomian(IDomainProvider,String,Object[])
	 */
	@SuppressWarnings("unchecked")
	public List findByHqlNameAndDomian(IDomainProvider domainProvider,
			String queryName, Object[] qryParams) throws DaoException,
			DaoConfigException {
		String hql = getHqlByDomainAndName(domainProvider, queryName);
		List result = null;
		try {
			result = getHibernateTemplate().find(hql, qryParams);
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
		return result;
	}

	/**
	 * @see platform.common.dao.IDao#findByHqlNameAndDomian(IDomainProvider,String,Object[])
	 */
	@SuppressWarnings("unchecked")
	public List findByHqlNameAndDomian(String queryName, Object[] qryParams)
			throws DaoException, DaoConfigException {
		String hql = getHqlByDomainAndName((IDomainProvider) this, queryName);
		List result = null;
		try {
			result = getHibernateTemplate().find(hql, qryParams);
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
		return result;
	}

	/**
	 * @see platform.common.dao.IDao#getHqlByDomainAndName(IDomainProvider,String)
	 */
	public String getHqlByDomainAndName(IDomainProvider domainProvider,
			String queryName) throws DaoConfigException {
		if (domainProvider == null) {
			throw this.handleDaoConfigException(new DaoConfigException("没有领域对象的提供者"));
		}
		String hql = domainProvider.getHqlByQryName(queryName);
		if (hql == null) {
			throw this.handleDaoConfigException(new DaoConfigException(
					"不能找到领域对象" + domainProvider.getDomainClassName() + "的名称为"
							+ queryName + "hql查询定义"));
		}
		return hql;
	}

	/**
	 * @see platform.common.dao.IDao#getHqlByDomainAndName(IDomainProvider,String)
	 */
	@SuppressWarnings("unchecked")
	public String getDynamicHqlByDomainAndName(IDomainProvider domainProvider,
			String queryName, Map dynamicMap) throws DaoConfigException {
		if (domainProvider == null) {
			throw this.handleDaoConfigException(new DaoConfigException(
					"没有领域对象的提供者"));
		}
		String hql = domainProvider.getDynamicHqlByQryName(queryName,
				dynamicMap);
		if (hql == null) {
			throw this.handleDaoConfigException(new DaoConfigException(
					"不能找到领域对象" + domainProvider.getDomainClassName() + "的名称为"
							+ queryName + "动态hql查询定义"));
		}
		return hql;
	}

	/**
	 * @see platform.common.dao.IDao#findUniqueByHqlNameAndDomian(domainProvider,String,Object[])
	 */
	@SuppressWarnings("unchecked")
	public Object findUniqueByHqlNameAndDomian(IDomainProvider domainProvider,
			String queryName, Object[] qryParams) throws DaoException,
			DaoConfigException {
		List resultList = findByHqlNameAndDomian(domainProvider, queryName,
				qryParams);
		if (resultList.size() > 0) {
			return resultList.get(0);
		}
		return null;
	}

	/**
	 * @see platform.common.dao.IDao#getDomainHqlProviderByName(String)
	 */
	/*
	 * private IDomainProvider getDomainHqlProviderByName(String domainName) {
	 * BeanWrapper bw = new BeanWrapperImpl(this); IDomainProvider
	 * domainHqlProvider = (IDomainProvider) bw .getPropertyValue(domainName);
	 * return domainHqlProvider; }
	 */
	/**
	 * @see platform.common.dao.IDao#findByHqlNameAndDomian(IDomainProvider,String,Map)
	 */
	@SuppressWarnings("unchecked")
	public List findByHqlNameAndDomian(IDomainProvider domainProvider,
			String queryName, Map qryParamMap) throws DaoException,
			DaoConfigException {
		String hql = getHqlByDomainAndName(domainProvider, queryName);
		List result = null;
		try {
			Query query = this.getSession().createQuery(hql);
			if (qryParamMap != null) {
				addArgs(qryParamMap, query);
			}
			result = query.list();
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
		return result;
	}
	
	@SuppressWarnings("unchecked")
	public List findByHqlNameAndDomian(String queryName, Map qryParamMap) throws DaoException,
			DaoConfigException {
		String hql = getHqlByDomainAndName((IDomainProvider)this, queryName);
		List result = null;
		try {
			Query query = this.getSession().createQuery(hql);
			if (qryParamMap != null) {
				addArgs(qryParamMap, query);
			}
			result = query.list();
		} catch (Exception e) {
			log.error("", e);
			throw this.handleDaoException(e);
		}
		return result;
	}

	/**
	 * 
	 * <br/>
	 * 方法描述:依据领域名称和查询定义别名查询记录,并按分页获取
	 * 
	 * @param domainProvider
	 *            领域提供者
	 * @param queryName
	 *            查询定义别名
	 * @param qryParamMap
	 *            查询参数Map
	 * @param page
	 *            分页对象
	 * @return list 领域列表
	 * @throws DaoException
	 * @throws DaoConfigException
	 */
	@SuppressWarnings("unchecked")
	public List findByHqlNameAndDomian(IDomainProvider domainProvider,
			String queryName, Map qryParamMap, Page page) throws DaoException,
			DaoConfigException {
		String hql = getHqlByDomainAndName(domainProvider, queryName);
		List result = null;
		try {
			Query query = this.getSession().createQuery(hql);
			if (qryParamMap != null) {
				addArgs(qryParamMap, query);
			}
			if (page.getPageNo() > 0 && page.getPageSize() > 0) {
				query.setFirstResult((page.getPageNo() - 1)
						* page.getPageSize());
				query.setMaxResults(page.getPageSize());
			}
			result = query.list();
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
		return result;
	}
	
	/**
	 * 
	 * <br/>
	 * 方法描述:依据领域名称和查询定义别名查询记录,并按分页获取
	 * 
	 * @param domainProvider
	 *            领域提供者
	 * @param queryName
	 *            查询定义别名
	 * @param qryParamMap
	 *            查询参数Map
	 * @param page
	 *            分页对象
	 * @return list 领域列表
	 * @throws DaoException
	 * @throws DaoConfigException
	 */
	@SuppressWarnings("unchecked")
	public List findByHqlNameAndDomian(String queryName, Map qryParamMap, Page page) throws DaoException,
			DaoConfigException {
		String hql = getHqlByDomainAndName((IDomainProvider)this, queryName);
		List result = null;
		try {
			Query query = this.getSession().createQuery(hql);
			if (qryParamMap != null) {
				addArgs(qryParamMap, query);
			}
			if (page.getPageNo() > 0 && page.getPageSize() > 0) {
				query.setFirstResult((page.getPageNo() - 1)
						* page.getPageSize());
				query.setMaxResults(page.getPageSize());
			}
			result = query.list();
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
		return result;
	}

	/**
	 * @see platform.common.dao.IDao#findUniqueByHqlNameAndDomian(IDomainProvider,String,Map)
	 */
	@SuppressWarnings("unchecked")
	public Object findUniqueByHqlNameAndDomian(IDomainProvider domainProvider,
			String queryName, Map qryParamMap) throws DaoException,
			DaoConfigException {
		List resultList = findByHqlNameAndDomian(domainProvider, queryName,
				qryParamMap);
		if (resultList.size() > 0) {
			return resultList.get(0);
		}
		return null;
	}
	
	/**
	 * @see platform.common.dao.IDao#findUniqueByHqlNameAndDomian(IDomainProvider,String,Map)
	 */
	@SuppressWarnings("unchecked")
	public Object findUniqueByHqlNameAndDomian(String queryName, Map qryParamMap) throws DaoException,
			DaoConfigException {
		List resultList = findByHqlNameAndDomian((IDomainProvider)this, queryName,qryParamMap);
		if (resultList.size() > 0) {
			return resultList.get(0);
		}
		return null;
	}

	/**
	 * 为hibernate查询设置参数
	 * 
	 * @param argMap
	 *            参数map
	 * @param query
	 *            hibernate查询
	 */
	@SuppressWarnings("unchecked")
	protected void addArgs(Map argMap, Query query) {
		Iterator itKey = argMap.keySet().iterator();
		while (itKey.hasNext()) {
			String key = (String) itKey.next();
			// Object arg = argMap.get(key);
			if (argMap.get(key) instanceof List) {
				query.setParameterList(key, (List) argMap.get(key));
			} else {
				query.setParameter(key, argMap.get(key));
			}
		}
	}

	/**
	 * @see platform.common.dao.IDao#newEmptyDomainObj(IDomainProvider)
	 */
	@SuppressWarnings("unchecked")
	public Object newEmptyDomainObj(IDomainProvider domainProvider)
			throws DaoConfigException {

		if (domainProvider == null) {
			throw new DaoConfigException("没有领域对象的提供者");
		}
		Class cls = domainProvider.getDomainClass();
		Object obj = null;
		if (cls != null) {
			try {
				obj = cls.newInstance();
			} catch (Exception e) {
				throw this.handleDaoConfigException(new DaoConfigException(
						"不能实例化名称为" + domainProvider.getDomainClassName()
								+ "的领域对象"));
			}
		}
		return obj;
	}

	@SuppressWarnings("unchecked")
	public void exeByHqlNameAndDomian(IDomainProvider domainProvider,
			String crudHqlName, Map hqlParamMap) throws DaoException,
			DaoConfigException {
		String hql = getHqlByDomainAndName(domainProvider, crudHqlName);
		try {
			Query query = this.getSession().createQuery(hql);
			if (hqlParamMap != null) {
				addArgs(hqlParamMap, query);
			}
			query.executeUpdate();
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
	}
	@SuppressWarnings("unchecked")
	public void exeByHqlNameAndDomian(String crudHqlName, Map hqlParamMap) throws DaoException,
			DaoConfigException {
		String hql = getHqlByDomainAndName((IDomainProvider)this, crudHqlName);
		try {
			Query query = this.getSession().createQuery(hql);
			if (hqlParamMap != null) {
				addArgs(hqlParamMap, query);
			}
			query.executeUpdate();
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
	}

	@SuppressWarnings( { "unchecked" })
	public List criteriaByQueryBeanAndDomian(IDomainProvider domainProvider,
			Object queryBean) throws DaoException, DaoConfigException {
		Criteria criteria = null;
		try {
			criteria = this.getSession().createCriteria(
					domainProvider.getDomainClass());

			BeanInfo beanInfo = Introspector.getBeanInfo(queryBean.getClass());
			PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
			for (int i = 0; i < pds.length; i++) {
				PropertyDescriptor pd = (PropertyDescriptor) pds[i];
				String propertyType = pd.getPropertyType().getSimpleName();
				String propertyName = pd.getDisplayName();// 获取属性名称
				Object propertyValue = PropertyUtils.getProperty(queryBean,
						propertyName);
				if (propertyValue instanceof Integer) {
					int tempValue = Util.toint(propertyValue);
					if (tempValue == -111111) {
						continue;
					}
				}
				if (propertyValue instanceof Double) {
					int tempValue = Util.toint(propertyValue);
					if (tempValue == -222222) {
						continue;
					}
				}
				if (Util.isNull(propertyValue))
					continue;// 处理字符串和list类型为空的情况

				if (propertyName.indexOf("like") == 0) {// 处理字符串匹配的情况
					String optProperty = propertyName.substring(4, 5)
							.toLowerCase()
							+ propertyName.substring(5);
					criteria.add(Expression.like(optProperty, PropertyUtils
							.getProperty(queryBean, propertyName).toString(),
							MatchMode.ANYWHERE));
					continue;
				}
				if (propertyName.indexOf("minEq") == 0) {// 处理小于等于的情况
					String optProperty = propertyName.substring(5, 6)
							.toLowerCase()
							+ propertyName.substring(6);
					criteria.add(Expression.le(optProperty, new Integer(
							PropertyUtils.getProperty(queryBean, propertyName)
									.toString())));
					continue;
				}
				if (propertyName.indexOf("min") == 0) {// 处理小于的情况
					String optProperty = propertyName.substring(3, 4)
							.toLowerCase()
							+ propertyName.substring(4);
					criteria.add(Expression.lt(optProperty, new Integer(
							PropertyUtils.getProperty(queryBean, propertyName)
									.toString())));
					continue;
				}
				if (propertyName.indexOf("maxEq") == 0) {// 处理大于等于的情况
					String optProperty = propertyName.substring(5, 6)
							.toLowerCase()
							+ propertyName.substring(6);
					criteria.add(Expression.ge(optProperty, new Integer(
							PropertyUtils.getProperty(queryBean, propertyName)
									.toString())));
					continue;
				}
				if (propertyName.indexOf("max") == 0) {// 处理大于的情况
					String optProperty = propertyName.substring(3, 4)
							.toLowerCase()
							+ propertyName.substring(4);
					criteria.add(Expression.gt(optProperty, new Integer(
							PropertyUtils.getProperty(queryBean, propertyName)
									.toString())));
					continue;
				}
				if (propertyName.indexOf("in") == 0) {// 处理大于的情况
					String optProperty = propertyName.substring(2, 3)
							.toLowerCase()
							+ propertyName.substring(3);
					criteria.add(Expression.in(optProperty,
							(Collection) PropertyUtils.getProperty(queryBean,
									propertyName)));
					continue;
				}
				if (propertyName.indexOf("eq") == 0) {// 处理小于等于的情况
					String optProperty = propertyName.substring(2, 3)
							.toLowerCase()
							+ propertyName.substring(3);
					if ("int".equals(propertyType)) {
						criteria.add(Expression.eq(optProperty, new Integer(
								PropertyUtils.getProperty(queryBean,
										propertyName).toString())));
						continue;
					}
					if ("double".equals(propertyType)) {
						criteria.add(Expression.eq(optProperty, new Double(
								PropertyUtils.getProperty(queryBean,
										propertyName).toString())));
						continue;
					}
					if ("String".equals(propertyType)) {
						criteria.add(Expression.eq(optProperty, PropertyUtils
								.getProperty(queryBean, propertyName)));
						continue;
					}
				}
			}
		} catch (IntrospectionException e) {
			log.error("动态创建bean错误", e);
			throw new TechnicalDaoException(e);
		} catch (IllegalAccessException e) {
			log.error("通过反射机制获取bean属性错误", e);
			throw new TechnicalDaoException(e);
		} catch (InvocationTargetException e) {
			log.error(e);
			throw new TechnicalDaoException(e);
		} catch (NoSuchMethodException e) {
			log.error("方法不匹配", e);
			throw new TechnicalDaoException(e);
		} catch (Exception e) {
			log.error("其他类型的异常。", e);
			throw new DaoException(e);
		}
		return criteria.list();
	}

	public Session getMySession() {
		return getSession();
	}

	public HibernateTemplate getMyHibernateTemplate() {
		return getHibernateTemplate();
	}

}
